<template>
  <div class="hv-designer-canvas-panel-container">
    <div
      class="hv-designer-canvas-panel-center"
      :class="[
        'hv-designer-canvas-panel-center',
        gridVisible
          ? 'hv-designer-canvas-panel-center-gird'
          : 'hv-designer-canvas-panel-center-default',
      ]"
    >
      <hv-rule-tool
        v-model:value="presetRefLine"
        ref="ruleToolRef"
        :contentLayout="contentLayout"
        parent
        isScaleRevise
        @lineToggle="refLineToggle($event, true)"
      >
        <div
          class="hv-designer-canvas-panel-main"
          v-hv-scrollbar="canvasPanelScrollOptions"
        >
          <div
            class="hv-designer-canvas-panel-workbanch"
            :style="canvasPanelWorkbanchStyle"
          >
            <hv-draggable-container
              ref="draggableContainerRef"
              :style="draggableContainerStyle"
            >
              <template v-if="componentTreeData && componentTreeData.length > 0">
                <hv-draggable-resizable
                  v-for="comp in componentTreeData[0].children"
                  :key="comp.id"
                  v-model:x="comp.componentPosition.left"
                  v-model:y="comp.componentPosition.top"
                  v-model:w="comp.componentDimensions.width"
                  v-model:h="comp.componentDimensions.height"
                  v-model:active="comp.active"
                  :initW="comp.componentDimensions.width"
                  :initH="comp.componentDimensions.height"
                  @drag-end="componentDragEnd($event, comp)"
                  :style="componentContainerStyle(comp)"
                >
                  <hv-designer-canvas-panel-comp-node :compData="comp" />
                </hv-draggable-resizable>
              </template>
            </hv-draggable-container>
          </div>
        </div>
      </hv-rule-tool>
    </div>
    <hv-designer-canvas-panel-bottom
      :canvasPercentage="canvasPercentage"
      @gridToggle="gridToggle"
      @refLineToggle="refLineToggle($event, false)"
      @clearRefLine="clearRefLine"
      @canvasPercentageChange="canvasPercentageChange"
      @canvasSelfAdaption="canvasResize"
      ref="canvasPanelBottomRef"
    />
  </div>
</template>

<script lang="ts">
import {
  computed,
  defineComponent,
  nextTick,
  onMounted,
  type PropType,
  reactive,
  ref,
  watch,
} from "vue";
import RuleTool from "@/visual/components/rule-tool/RuleTool.vue";
import {
  DraggableContainer,
  DraggableResizable,
} from "@/visual/components/draggable-resizable";
import CommonUtil from "@/visual/commons/utils/common-util";
import DesignerCanvasPanelBottom from "./DesignerCanvasPanelBottom.vue";
import DesignerCanvasPanelCompNode from "./DesignerCanvasPanelCompNode.vue";
import { cloneDeep } from "lodash";
import { canvasPanelConfig, layerPanelConfig, visualComponents, visualStyleGenerator } from "@/visual/configs/dashboard-conifg";
import type DashboardCanvasType from "@/visual/commons/types/dashboard-canvas-type";
import type DashboardComponentType from "@/visual/commons/types/dashboard-component-type";
import useWindowResize from "@/visual/hooks/use-window-resize";
import DashboardEvent from "@/visual/events/dashboard-event";
import DashboardUtil from "@/visual/commons/utils/dashboard-util";

export default defineComponent({
  name: "DesignerCanvasPanel",
  components: {
    hvRuleTool: RuleTool,
    hvDraggableContainer: DraggableContainer,
    hvDraggableResizable: DraggableResizable,
    hvDesignerCanvasPanelBottom: DesignerCanvasPanelBottom,
    hvDesignerCanvasPanelCompNode: DesignerCanvasPanelCompNode,
    ...visualComponents,
  },
  props: {
    canvasData: {
      type: Object as PropType<DashboardCanvasType>,
      default: () => {},
    },
    componentData: {
      type: Array as PropType<DashboardComponentType<any>[]>,
      defult: () => [],
    },
  },
  setup(props) {
    //参考线
    const presetRefLine = ref([]);

    //拖拽容器ref
    const draggableContainerRef = ref();

    //标尺ref
    const ruleToolRef = ref();

    //画布底部ref
    const canvasPanelBottomRef = ref();

    //网格是否显示
    const gridVisible = ref(true);

    //参考线是否显示
    const refLineVisible = ref(true);

    //画布百分比
    const canvasPercentage = ref(canvasPanelConfig.percentageMaxVal);

    //画布缩放
    const canvasScale = ref(1);

    // 内容的位置
    const contentLayout = reactive({
      left: 0,
      top: 0,
    });

    //图层类型
    const layerTreeNodeType = ref<any>(layerPanelConfig.layerTreeNodeType);

    /**
     * 画布外层容器滚动条
     */
    const canvasPanelScrollOptions = reactive({
      className: "os-theme-light",
      overflowBehavior: {
        y: "scroll",
        x: "scroll",
      },
      scrollbars: {
        autoHide: "never",
      },
    });

    //画布工作台间距
    const canvasPanelWorkbanchSpacing: any = reactive({
      x: 0,
      y: 0,
    });

    //组件树
    const componentTreeData: any = ref<DashboardComponentType<any>[]>([]);

    /**
     * 画布工作台样式
     */
    const canvasPanelWorkbanchStyle: any = computed(() => {
      const { x, y } = canvasPanelWorkbanchSpacing;
      return {
        width: props.canvasData.canvasDimensions.width * canvasScale.value + "px",
        height: props.canvasData.canvasDimensions.height * canvasScale.value + "px",
        marginLeft: x + "px",
        marginTop: y + "px",
        borderTopColor: props.canvasData.styleProps?.borderTop?.color,
        borderTopWidth: props.canvasData.styleProps?.borderTop?.width + "px",
        borderTopStyle: props.canvasData.styleProps?.borderTop?.style,
        borderBottomColor: props.canvasData.styleProps?.borderBottom?.color,
        borderBottomWidth: props.canvasData.styleProps?.borderBottom?.width + "px",
        borderBottomStyle: props.canvasData.styleProps?.borderBottom?.style,
        borderLeftColor: props.canvasData.styleProps?.borderLeft?.color,
        borderLeftWidth: props.canvasData.styleProps?.borderLeft?.width + "px",
        borderLeftStyle: props.canvasData.styleProps?.borderLeft?.style,
        borderRightColor: props.canvasData.styleProps?.borderRight?.color,
        borderRightWidth: props.canvasData.styleProps?.borderRight?.width + "px",
        borderRightStyle: props.canvasData.styleProps?.borderRight?.style,
        backgroundColor: props.canvasData.styleProps?.backgroundColor,
        backgroundImage: props.canvasData.styleProps?.backgroundImage
          ? `url(${props.canvasData.styleProps?.backgroundImage})`
          : "",
        backgroundRepeat: props.canvasData.styleProps?.backgroundRepeat,
        backgroundSize:
          props.canvasData.styleProps?.backgroundSize &&
          props.canvasData.styleProps?.backgroundSize.length > 1
            ? `${props.canvasData.styleProps?.backgroundSize[0]}  ${props.canvasData.styleProps?.backgroundSize[1]}`
            : "",
        backgroundPosition:
          props.canvasData.styleProps?.backgroundPosition &&
          props.canvasData.styleProps?.backgroundPosition.length > 1
            ? `${props.canvasData.styleProps?.backgroundPosition[0]}  ${props.canvasData.styleProps?.backgroundPosition[1]}`
            : "",
      };
    });

    /**
     * 拖拽框样式
     */
    const draggableContainerStyle = computed(() => {
      return {
        transform: `scale(${canvasScale.value}, ${canvasScale.value})`,
        transformOrigin: `0% 0%`,
        width: props.canvasData.canvasDimensions.width + "px",
        height: props.canvasData.canvasDimensions.height + "px",
      };
    });

    /**
     * 窗口发现改变
     */
    useWindowResize(true, () => {
      canvasResize();
    });

    /**
     * 获取拖拽的容器
     */
    const getDraggableContainerEl = () => {
      return draggableContainerRef.value.$el;
    };

    /**
     * 边界检查
     */
    const checkBoundary = (
      payload: { x: number; y: number },
      comp: DashboardComponentType<any>
    ) => {
      //边界处理
      if (payload.x <= 0) {
        comp.componentPosition.left = 0;
      }

      if (payload.y <= 0) {
        comp.componentPosition.top = 0;
      }

      nextTick(() => {
        const dimensions = CommonUtil.getElemenDimensions(getDraggableContainerEl());
        const draggableContainerHeight = dimensions.height;
        const draggableContainerWidth = dimensions.width;

        if (payload.x + comp.componentDimensions.width >= draggableContainerWidth) {
          comp.componentPosition.left =
            draggableContainerWidth - comp.componentDimensions.width - 1;
        }

        if (payload.y + comp.componentDimensions.height >= draggableContainerHeight) {
          comp.componentPosition.top =
            draggableContainerHeight - comp.componentDimensions.height - 1;
        }
      });
    };

    /**
     * 组件组件拖动结束
     */
    const componentDragEnd = (
      payload: { x: number; y: number },
      comp: DashboardComponentType<any>
    ) => {
      checkBoundary(payload, comp);
    };

    /**
     * 网格切换
     */
    const gridToggle = (val: boolean) => {
      gridVisible.value = val;
    };

    /**
     * 参考线切换
     */
    const refLineToggle = (val: boolean, hasRuleTool: boolean) => {
      hasRuleTool
        ? canvasPanelBottomRef.value.setRefLineVisible(val)
        : ruleToolRef.value.setLineVisible(val);
    };

    const lineToggle = (val: boolean) => {
      console.info(val);
      refLineVisible.value = val;
    };

    /**
     * 清除参考线
     */
    const clearRefLine = () => {
      ruleToolRef.value.clearLine();
    };

    /**
     * 画布百分比
     */
    const canvasPercentageChange = (v: number) => {
      canvasScale.value = v / 100;
      calcCanvasSpacing();
    };

    /**
     * 计算画布上与左距离
     * 画布上与左距离=(外框宽与高-(画布的宽高*宽高缩放比))/2
     */
    const calcCanvasSpacing = (contentDimensions: any = undefined) => {
      const dimensions = contentDimensions
        ? contentDimensions
        : ruleToolRef.value.getContentDimensions();
      const width =
        dimensions.width - props.canvasData.canvasDimensions.width * canvasScale.value;
      const height =
        dimensions.height - props.canvasData.canvasDimensions.height * canvasScale.value;
      canvasPanelWorkbanchSpacing.x = width > 0 ? width / 2 : 0;
      canvasPanelWorkbanchSpacing.y = height > 0 ? height / 2 : 0;
    };

    /**
     * 画布resize
     * 宽高缩放=将外框的宽高/(画布的宽高+缩放间隔值)
     * 重新计算画布上与左距离=(外框宽与高-(画布的宽与高*宽与高缩放比))/2
     */
    const canvasResize = () => {
      //兼容
      if (!ruleToolRef.value) {
        return;
      }

      const dimensions = ruleToolRef.value.getContentDimensions();

      //计算宽高缩放值
      const widthScale =
        dimensions.width /
        (props.canvasData.canvasDimensions.width + canvasPanelConfig.scaleSpacingValue);
      const heightScale =
        dimensions.height /
        (props.canvasData.canvasDimensions.height + canvasPanelConfig.scaleSpacingValue);

      //将宽高缩放最小值作为画布百分比
      let percentage = Math.round(Math.min(widthScale, heightScale) * 100);
      if (percentage < canvasPanelConfig.percentageMinVal) {
        percentage = canvasPanelConfig.percentageMinVal;
      }

      if (percentage > canvasPanelConfig.percentageMaxVal) {
        percentage = canvasPanelConfig.percentageMaxVal;
      }

      canvasPercentage.value = percentage;
      canvasScale.value = canvasPercentage.value / 100;
      //计算画布上与左距离
      calcCanvasSpacing(dimensions);
    };

    /**
     * 初始化
     */
    const init = () => {
      //组件添加
      DashboardEvent.getCurrentInsertComponentEvent().subscribe(
        (currentComponent: any) => {
          const comp: any = {
            ...currentComponent,
            layerType: layerTreeNodeType.value.layer,
            level: 1,
          };

          componentTreeData.value[0].children.push(comp);
        }
      );

      //图层组添加
      DashboardEvent.getAddLayerGroupEvent().subscribe((currentComponent: any) => {
        const newGroupComp = cloneDeep(layerPanelConfig.groupComponent);
        const comp: any = {
          id: currentComponent.id,
          name: newGroupComp.name,
          label: currentComponent.label,
          group: newGroupComp.group,
          layerType: currentComponent.type,
          level: currentComponent.level,
          propertyComponent: newGroupComp.propertyComponent,
          componentDimensions: newGroupComp.componentDimensions,
          componentPosition: newGroupComp.componentPosition,
          styleProps: newGroupComp.styleProps,
          componentStyleGeneratorName: newGroupComp.componentStyleGeneratorName,
        };

        componentTreeData.value[0].children.push(comp);
      });

      //添加子图层组
      DashboardEvent.getAddSubLayerGroupEvent().subscribe((data: any) => {
        const newGroupComp = cloneDeep(layerPanelConfig.groupComponent);
        const comp: any = {
          id: data.newGroupComp.id,
          name: newGroupComp.name,
          label: data.newGroupComp.label,
          group: newGroupComp.group,
          layerType: data.newGroupComp.type,
          level: data.newGroupComp.level,
          propertyComponent: newGroupComp.propertyComponent,
          componentDimensions: newGroupComp.componentDimensions,
          componentPosition: newGroupComp.componentPosition,
          styleProps: newGroupComp.styleProps,
          componentStyleGeneratorName: newGroupComp.componentStyleGeneratorName,
        };

        if (data.parentComp.id === layerPanelConfig.layerTreeRootNodeId) {
          componentTreeData.value[0].children.push(comp);
        } else {
          const promise = new Promise((resolve) => {
            DashboardUtil.getTreeNodeById(
              componentTreeData.value[0].children,
              data.parentComp.id,
              resolve
            );
          });

          promise.then((parentComp: any) => {
            //判断children是否存在
            parentComp && !parentComp.children && (parentComp.children = []);
            parentComp.children.push(comp);
          });
        }
      });

      //图层移动后组件的处理
      DashboardEvent.getDragLayerNodeEvent().subscribe((data: any) => {
        // 如果移动到根目录中
        if (data.dropComp.id === layerPanelConfig.layerTreeRootNodeId) {
          //删除拖动的原组件
          DashboardUtil.deleteTreeChildrenNodeById(
            componentTreeData.value[0].children,
            data.dragComp.id,
            (delNode: any) => {
              componentTreeData.value[0].children.push({
                ...delNode,
                level: 1,
              });
            }
          );
          //如果移动到非根目录
        } else {
          const promise = new Promise((resolve) => {
            DashboardUtil.getTreeNodeById(
              componentTreeData.value[0].children,
              data.dropComp.id,
              resolve
            );
          });

          promise.then((dropComp: any) => {
            //判断children是否存在
            dropComp && !dropComp.children && (dropComp.children = []);

            //删除拖动的原组件
            DashboardUtil.deleteTreeChildrenNodeById(
              componentTreeData.value[0].children,
              data.dragComp.id,
              (delNode: any) => {
                //根据放下的组件添加
                dropComp.children.push({
                  ...delNode,
                  level: dropComp.level + 1,
                });
              }
            );
          });
        }
      });

      //删除图层对应的节点
      DashboardEvent.getDeleteLayerNodeEvent().subscribe((delNode: any) => {
        DashboardUtil.deleteTreeChildrenNodeById(
          componentTreeData.value[0].children,
          delNode.id,
          () => {}
        );
      });

      //图层上移
      DashboardEvent.getLayerUpEvent().subscribe((currentComponent: any) => {
        DashboardUtil.treeNodeUpById(
          componentTreeData.value[0].children,
          currentComponent.id,
          () => {}
        );
      });

      //图层下移
      DashboardEvent.getLayerDownEvent().subscribe((currentComponent: any) => {
        DashboardUtil.treeNodeDownById(
          componentTreeData.value[0].children,
          currentComponent.id,
          () => {}
        );
      });

      //图层置顶
      DashboardEvent.getLayerToTopEvent().subscribe((currentComponent: any) => {
        DashboardUtil.treeNodeToTopById(
          componentTreeData.value[0].children,
          currentComponent.id,
          () => {}
        );
      });

      //图层置底
      DashboardEvent.getLayerToBottomEvent().subscribe((currentComponent: any) => {
        DashboardUtil.treeNodeToBottomById(
          componentTreeData.value[0].children,
          currentComponent.id,
          () => {}
        );
      });

      //图层移动到指定位置
      DashboardEvent.getLayerMoveToIndexEvent().subscribe((data: any) => {
        console.info(data);
        DashboardUtil.treeNodeMoveToIndex(
          componentTreeData.value[0].children,
          data.dragComp.id,
          data.targetIndex,
          () => {}
        );
      });

      //图层复制
      DashboardEvent.getLayerCopyEvent().subscribe((data: any) => {
        const comp = data.comp;

        DashboardUtil.getTreeNodeIndexAndListById(
          componentTreeData.value[0].children,
          comp.id,
          (index: number, list: any) => {
            const currentComp = list[index];

            const newComp: any = cloneDeep(currentComp);
            (newComp.id = data.id),
              (newComp.label = currentComp.label + "副本" + data.id);

            currentComp.children && (newComp.children = []);
            DashboardUtil.treeInsertNode(list, index + 1, newComp);
          }
        );
      });

      //激活当前图层
      DashboardEvent.getActiveCurrentLayerEvent().subscribe((currentComponent: any) => {
        componentTreeData.value[0].children.map((item) => {
          item.active = item.id === currentComponent.id ? true : false;
        });
      });

      //图层重命名
      DashboardEvent.getLayerRenameEvent().subscribe((currentComponent: any) => {
        const promise = new Promise((resolve) => {
          DashboardUtil.getTreeNodeById(
            componentTreeData.value[0].children,
            currentComponent.id,
            resolve
          );
        });

        promise.then((comp: any) => {
          comp.label = currentComponent.label;
        });
      });
    };

    /**
     * 组件容器样式
     */
    const componentContainerStyle = (comp: DashboardComponentType<any>) => {
      return visualStyleGenerator[
        comp.componentStyleGeneratorName
      ].containerStyleGenerator(comp);
    };

    watch(
      () => props.componentData,
      (val) => {
        if (val) {
          componentTreeData.value = val;
        }
      },
      { deep: true, immediate: true }
    );

    watch(
      () => props.canvasData.canvasDimensions,
      (val) => {
        if (val) {
          canvasResize();
        }
      },
      { deep: true, immediate: false }
    );

    onMounted(() => {
      init();
    });

    return {
      canvasPanelScrollOptions,
      presetRefLine,
      contentLayout,
      componentDragEnd,
      draggableContainerRef,
      canvasPanelBottomRef,
      ruleToolRef,
      componentTreeData,
      gridVisible,
      gridToggle,
      refLineVisible,
      canvasPercentage,
      refLineToggle,
      clearRefLine,
      canvasPercentageChange,
      canvasPanelWorkbanchStyle,
      draggableContainerStyle,
      canvasResize,
      lineToggle,
      componentContainerStyle,
    };
  },
});
</script>

<style lang="less" scoped>
@import "./DesignerCanvasPanel.less";
</style>
